package com.information.manage.basic.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.information.manage.basic.common.constant.Constant;
import com.information.manage.basic.common.utils.RedisUtils;
import com.information.manage.basic.mapper.*;
import com.information.manage.basic.model.dto.UserMenuDTO;
import com.information.manage.basic.model.dto.UserMenuListDTO;
import com.information.manage.basic.model.entity.*;
import com.information.manage.basic.model.query.*;
import com.information.manage.basic.model.vo.FindUserVO;
import com.information.manage.basic.model.vo.LoginUserInfoVO;
import com.information.manage.basic.model.vo.SysUserPageVO;
import com.information.manage.basic.model.vo.UserMenuVO;
import com.information.manage.basic.common.recycling.RecoveryDataOperateManager;
import com.information.manage.basic.common.result.Result;
import com.information.manage.basic.common.result.enums.ResEnum;
import com.information.manage.basic.common.token.TokenUtils;
import com.information.manage.basic.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings("all")
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserDO> implements SysUserService {

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private RecoveryDataOperateManager recoveryDataOperateManager;

    @Resource
    private StaffMapper staffMapper;

    @Resource
    private SysRelUserRoleMapper sysRelUserRoleMapper;

    @Resource
    private SysRelRoleMenuMapper sysRelRoleMenuMapper;

    @Resource
    private SysRelUserStaffMapper sysRelUserStaffMapper;

    @Resource
    private RelStaffDepMapper relStaffDepMapper;

    @Resource
    private SysRelUserMenuMapper sysRelUserMenuMapper;

    @Resource
    private SysMenuMapper sysMenuMapper;

    @Resource
    private DepartmentMapper departmentMapper;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private SysRoleMapper sysRoleMapper;

    private static final String curLoginUserPre = "cur_login_user_";

    // 用户登录设置过期时间
    private static final long EXPIRE_DATE = 24 * 60 * 60 * 1000;  // 先设置为一天

    @Override
    public Result login(LoginQuery loginQuery) {
        SysUserDO sysUserDO = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUserDO>().eq(SysUserDO::getName, loginQuery.getUsername()).last("limit 1"));
        if (sysUserDO == null){ //用户不存在
            return new Result(ResEnum.USER_NOT_EXIST.getCode(), ResEnum.USER_NOT_EXIST.getMsg());
        }
        if (sysUserDO.getPassword() != null){
            if (!sysUserDO.getPassword().equals(loginQuery.getPassword())){ //判断密码是否相同
                // 密码不同
                return new Result(ResEnum.PASSWORD_NOT_RIGHT.getCode(), ResEnum.PASSWORD_NOT_RIGHT.getMsg());
            }
        }
        Integer id = sysUserDO.getId();
        // 判断该用户是否已经登录过
        if (redisUtils.exists(curLoginUserPre + id)){
            try {
                if (TokenUtils.verify(redisUtils.get(curLoginUserPre + id).toString())) {
                    // 已登录的直接先返回token
                    return new Result<>(redisUtils.get(curLoginUserPre + id));
                }
            } catch (Exception e) {
                // 未登录过，生成token
                log.error("令牌过期，重新登录:{}", e.getMessage());
                // 删除redis中已登录的token
                redisUtils.remove(curLoginUserPre + id);
                // 修改用户的登录时间
                sysUserDO.setLastLoginTime(LocalDateTime.now());
                sysUserMapper.updateById(sysUserDO);
                long expireTime = System.currentTimeMillis() + EXPIRE_DATE;
                // 生成token
                String token = TokenUtils.token(sysUserDO.getId(), sysUserDO.getName(), expireTime);
                // 存储当前登录的用户
                redisUtils.set(curLoginUserPre + id, token, expireTime, TimeUnit.MILLISECONDS);
                return Result.ok(token);
            }
        }
        // 修改用户的登录时间
        sysUserDO.setLastLoginTime(LocalDateTime.now());
        sysUserMapper.updateById(sysUserDO);
        long expireTime = System.currentTimeMillis() + EXPIRE_DATE;
        // 生成token
        String token = TokenUtils.token(sysUserDO.getId(), sysUserDO.getName(), expireTime);
        // 存储当前登录的用户
        redisUtils.set(curLoginUserPre + id, token, expireTime, TimeUnit.MILLISECONDS);
        return Result.ok(token);
    }

    @Override
    public LoginUserInfoVO getUserInfo(Integer userId) {
        SysUserDO sysUserDO = sysUserMapper.selectById(userId);
        sysUserDO.setPassword("");
        LoginUserInfoVO loginUserInfoVO = new LoginUserInfoVO();
        BeanUtils.copyProperties(sysUserDO, loginUserInfoVO);
        SysRelUserStaffDO sysRelUserStaffDO = sysRelUserStaffMapper.selectOne(new LambdaQueryWrapper<SysRelUserStaffDO>().eq(SysRelUserStaffDO::getUserId, userId));
        if (sysRelUserStaffDO != null){
            StaffDO staffDO = staffMapper.selectById(sysRelUserStaffDO.getStaffId());
            if (staffDO != null){
                loginUserInfoVO.setStaffId(staffDO.getId());
                loginUserInfoVO.setStaffName(staffDO.getName());
                RelStaffDepDO relStaffDepDO = relStaffDepMapper.selectOne(new LambdaQueryWrapper<RelStaffDepDO>().eq(RelStaffDepDO::getStaffId, staffDO.getId()));
                if (relStaffDepDO != null){
                    DepartmentDO departmentDO = departmentMapper.selectById(relStaffDepDO.getDepartmentId());
                    if (departmentDO != null){
                        loginUserInfoVO.setDepartmentId(departmentDO.getId());
                        loginUserInfoVO.setDepartmentName(departmentDO.getName());
                    }
                }
            }
        }

        SysRelUserRoleDO sysRelUserRoleDO = sysRelUserRoleMapper.selectOne(new LambdaQueryWrapper<SysRelUserRoleDO>().eq(SysRelUserRoleDO::getUserId, userId));
        if (sysRelUserRoleDO != null){
            SysRoleDO sysRoleDO = sysRoleMapper.selectById(sysRelUserRoleDO.getRoleId());
            if (sysRoleDO != null){
                loginUserInfoVO.setRoleId(sysRoleDO.getId());
                loginUserInfoVO.setRoleName(sysRoleDO.getName());
            }
        }

        return loginUserInfoVO;
    }

    @Override
    public void loginOut(Integer userId) {
        redisUtils.remove(curLoginUserPre + userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Integer> addUser(SysUserAddQuery sysUserAddQuery) {
        // 判断用户是否存在
        Integer count = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUserDO>()
                .eq(SysUserDO::getName, sysUserAddQuery.getUserName()));
        if (count != 0) return Result.fail(ResEnum.USER_IS_EXIST.getCode(), "该用户名已存在");
        // 添加用户数据
        SysUserDO sysUserDO = new SysUserDO();
        sysUserDO.setName(sysUserAddQuery.getUserName());
        sysUserDO.setDescription(sysUserAddQuery.getDescription());
        sysUserDO.setPassword(sysUserAddQuery.getPassword());
        sysUserDO.setCreateTime(LocalDateTime.now());
        sysUserMapper.insert(sysUserDO);
        Integer userId = sysUserDO.getId();
        if (sysUserAddQuery.getRoleId() != null){
            // 保存用户角色
            SysRelUserRoleDO sysRelUserRoleDO = new SysRelUserRoleDO();
            sysRelUserRoleDO.setUserId(userId);
            sysRelUserRoleDO.setRoleId(sysUserAddQuery.getRoleId());
            sysRelUserRoleMapper.insert(sysRelUserRoleDO);
            this.setUserMenu(userId, sysUserAddQuery.getRoleId());
        }
        return Result.ok(userId);

    }

    /**
     * 为用户设置角色对应的菜单权限
     * @param userId 用户id
     * @param roleId 角色id
     */
    private void setUserMenu(Integer userId, Integer roleId){
        // 根据roleId获取menuIds
        List<Integer> menuIds = sysRelRoleMenuMapper.selectObjs(new LambdaQueryWrapper<SysRelRoleMenuDO>()
                .select(SysRelRoleMenuDO::getMenuId)
                .eq(SysRelRoleMenuDO::getRoleId, roleId)).stream().map(it -> (Integer) it).collect(Collectors.toList());
        // 删除用户对应的用户菜单数据
        sysRelUserMenuMapper.delete(new LambdaQueryWrapper<SysRelUserMenuDO>().eq(SysRelUserMenuDO::getUserId, userId));
        // 为用户分配角色对应的菜单权限, 保存数据到用户菜单中间表
        if (menuIds.size() > 0) {
            this.saveUserMenu(Arrays.asList(userId), menuIds);
        }
    }

    public boolean saveUserMenu(List<Integer> userIds, List<Integer> menuIds) {
        for (Integer userId : userIds) {
            for (Integer menuId : menuIds) {
                SysRelUserMenuDO userMenuDO = new SysRelUserMenuDO();
                userMenuDO.setUserId(userId);
                //获取菜单pid
                Integer pid = (Integer) sysMenuMapper.selectObjs(new LambdaQueryWrapper<SysMenuDO>()
                        .select(SysMenuDO::getParentId)
                        .eq(SysMenuDO::getId, menuId)).stream().findFirst().orElse(null);
                if (pid == null) continue;  // 未找到，说明已经删除，直接跳过
                userMenuDO.setMenuParentId(pid);
                userMenuDO.setMenuId(menuId);
                // 获取当前用户对应的菜单的层级
                Integer sequence = (Integer) sysRelUserMenuMapper.selectObjs(new LambdaQueryWrapper<SysRelUserMenuDO>()
                        .select(SysRelUserMenuDO::getSequence)
                        .eq(SysRelUserMenuDO::getUserId, userId)
                        .eq(SysRelUserMenuDO::getMenuParentId, pid)
                        .orderByDesc(SysRelUserMenuDO::getSequence)).stream().findFirst().orElse(null);
                if (sequence == null) userMenuDO.setSequence(1);
                else userMenuDO.setSequence(++sequence); //存在时层数+1
                sysRelUserMenuMapper.insert(userMenuDO);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Integer> updateUser(SysUserUpdateQuery sysUserUpdateQuery) {
        Integer userId = sysUserUpdateQuery.getUserId();
        //判断用户是否存在
        Integer count = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUserDO>()
                .eq(SysUserDO::getName, sysUserUpdateQuery.getUserName())
                .ne(SysUserDO::getId, userId));
        if (count != 0) return Result.fail(ResEnum.USER_IS_EXIST.getCode(), "该用户名已存在");

        // 修改用户数据
        SysUserDO sysUserDO = new SysUserDO();
        sysUserDO.setId(userId);
        sysUserDO.setName(sysUserUpdateQuery.getUserName());
        sysUserDO.setDescription(sysUserUpdateQuery.getDescription());
        sysUserDO.setPassword(sysUserUpdateQuery.getPassword());
        sysUserDO.setUpdateTime(LocalDateTime.now());
        sysUserMapper.updateById(sysUserDO);
        // 修改用户角色
        if (sysUserUpdateQuery.getRoleId() != null){
            sysRelUserRoleMapper.update(null, new LambdaUpdateWrapper<SysRelUserRoleDO>()
                    .eq(SysRelUserRoleDO::getUserId, userId)
                    .set(SysRelUserRoleDO::getRoleId, sysUserUpdateQuery.getRoleId()));
            this.setUserMenu(userId, sysUserUpdateQuery.getRoleId());
        }
        redisUtils.remove(Constant.userHotDataKeyPrex + userId);
        return Result.ok(userId);
    }

    @Override
    public Result<Boolean> userBindStaff(Integer staffId, Integer userId) {
        Integer sAUCount = sysRelUserStaffMapper.selectCount(new LambdaQueryWrapper<SysRelUserStaffDO>().eq(SysRelUserStaffDO::getStaffId, staffId));
        Integer uASCount = sysRelUserStaffMapper.selectCount(new LambdaQueryWrapper<SysRelUserStaffDO>().eq(SysRelUserStaffDO::getUserId, userId));
        if (sAUCount != 0){   //判断员工是否被用户绑定， 如果员工已经绑定了一个用户 则不允许被再次绑定  （即根据员工查询存在）
            return Result.fail("该员工已经绑定了一个用户，不能再次绑定");
        }else if (uASCount != 0){   //判断该用户是否已经绑定了员工  如果绑定了员工，则修改该用户绑定的员工   （即根据用户查询存在）
            sysRelUserStaffMapper.update(null, new LambdaUpdateWrapper<SysRelUserStaffDO>()
                    .eq(SysRelUserStaffDO::getUserId, userId)
                    .set(SysRelUserStaffDO::getStaffId, staffId));
        }else {  // 都未存在时 直接添加绑定
            SysRelUserStaffDO sysRelUserStaffDO = new SysRelUserStaffDO();
            sysRelUserStaffDO.setUserId(userId);
            sysRelUserStaffDO.setStaffId(staffId);
            sysRelUserStaffMapper.insert(sysRelUserStaffDO);
        }
        // 处理完成需要清除用户热点数据
        redisUtils.remove(Constant.userHotDataKeyPrex + userId);
        return Result.ok(true);
    }

    @Override
    public Boolean deleteUser(List<Integer> userIds) {
//        recoveryDataOperateManager.deleteData(SysUserDO.class, userIds);
        sysUserMapper.deleteBatchIds(userIds);
        for (Integer userId : userIds) {
            redisUtils.remove(Constant.userHotDataKeyPrex + userId);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean moveUserMenu(Integer userId, Integer menuId, boolean up) {
        //获取当前层级的数据
        UserMenuDTO curData = new UserMenuDTO();
        curData.setUserId(userId);
        curData.setMenuId(menuId);
        Integer menuPId = (Integer) sysRelUserMenuMapper.selectObjs(new LambdaQueryWrapper<SysRelUserMenuDO>()
                .select(SysRelUserMenuDO::getMenuParentId)
                .eq(SysRelUserMenuDO::getUserId, userId)
                .eq(SysRelUserMenuDO::getMenuId, menuId).last("limit 1")).stream().findFirst().orElse(null);
        curData.setMenuParentId(menuPId);
        Integer menuSequence = (Integer) sysRelUserMenuMapper.selectObjs(new LambdaQueryWrapper<SysRelUserMenuDO>()
                .select(SysRelUserMenuDO::getSequence)
                .eq(SysRelUserMenuDO::getUserId, userId)
                .eq(SysRelUserMenuDO::getMenuId, menuId).last("limit 1")).stream().findFirst().orElse(null);
        curData.setSequence(menuSequence);
        int order;
        if (up){ //上移
            //获取上一层级
            order = curData.getSequence() - 1;
        }else { //下移
            //获取下一层级
            order = curData.getSequence() + 1;
        }
        UserMenuDTO upOrDownData = new UserMenuDTO();
        upOrDownData.setUserId(userId);
        Integer newMenuId = (Integer) sysRelUserMenuMapper.selectObjs(new LambdaQueryWrapper<SysRelUserMenuDO>()
                .select(SysRelUserMenuDO::getMenuId)
                .eq(SysRelUserMenuDO::getUserId, userId)
                .eq(SysRelUserMenuDO::getMenuParentId, curData.getMenuParentId())
                .eq(SysRelUserMenuDO::getSequence, order).last("limit 1")).stream().findFirst().orElse(null);
        upOrDownData.setMenuId(newMenuId);
        upOrDownData.setMenuParentId(curData.getMenuParentId());
        upOrDownData.setSequence(order);
        //上移时不存在上一层级 下移时不存在下一层级  不能移动
        if (upOrDownData == null || upOrDownData.getSequence() == null) return false;
        //调换排序数据 order 更新
        //tmp 临时存储
        int tmp;
        tmp = curData.getSequence();
        curData.setSequence(upOrDownData.getSequence());
        upOrDownData.setSequence(tmp);
        sysRelUserMenuMapper.update(null, new LambdaUpdateWrapper<SysRelUserMenuDO>()
                .eq(SysRelUserMenuDO::getUserId, curData.getUserId())
                .eq(SysRelUserMenuDO::getMenuId, curData.getMenuId())
                .set(SysRelUserMenuDO::getSequence, curData.getSequence()));
        sysRelUserMenuMapper.update(null, new LambdaUpdateWrapper<SysRelUserMenuDO>()
                .eq(SysRelUserMenuDO::getUserId, upOrDownData.getUserId())
                .eq(SysRelUserMenuDO::getMenuId, upOrDownData.getMenuId())
                .set(SysRelUserMenuDO::getSequence, upOrDownData.getSequence()));
        return true;
    }

    @Override
    public Result<Page<SysUserPageVO>> queryUserPage(SysUserPageQuery sysUserPageQuery) {
        Page<SysUserPageVO> sysUserPageVOPage = sysUserMapper.queryUserPage(new Page(sysUserPageQuery.getCurPage(), sysUserPageQuery.getPageSize())
                , sysUserPageQuery.getUserName(), sysUserPageQuery.getDescription(), sysUserPageQuery.getRoleId(), sysUserPageQuery.getStaffName()
                , sysUserPageQuery.getStartTime(), sysUserPageQuery.getEndTime());
        for (SysUserPageVO record : sysUserPageVOPage.getRecords()) {
            record.setPassword("");
        }
        return Result.ok(sysUserPageVOPage);
    }

    @Override
    public Result<List<UserMenuVO>> getUserMenu(HttpServletRequest request, UserMenuQuery userMenuQuery) {
//        Integer userId = request.getIntHeader("token");
        Integer userId = TokenUtils.getUserId(StringUtils.isEmpty(request.getHeader("token")) ? request.getAttribute("token").toString() : request.getHeader("token"));
        //获取该用户对应的角色名称
//        String roleName = middleMapper.queryRoleStringByUserId(userId);
//        if ("admin".equals(roleName)){  //当该用户对应的角色名称为admin时 默认查出所有的菜单权限
//            userId = null;
//        }
        //获取满足条件的用户菜单
        List<UserMenuListDTO> userMenuList = sysUserMapper.findUserMenuList(userId, userMenuQuery.getDescription(), userMenuQuery.getName());
        System.out.println("测试菜单");
        System.out.println(userMenuList.toString());
        // menuId -> 菜单信息
        Map<Integer, UserMenuListDTO> map = userMenuList.stream().collect(Collectors.toMap(it -> it.getMenuId(), it -> it));
        List<UserMenuVO> resultList  = recursionGetMenu(map, null);
        return Result.ok(resultList);
    }

    /**
     * 递归获取菜单
     * @param map 所有的菜单map 菜单id -》 菜单信息
     * @param curMenuId 当前菜单id
     * @return
     */
    private List<UserMenuVO> recursionGetMenu(Map<Integer, UserMenuListDTO> map, Integer curMenuId){
        boolean flag = true;  //定义是否存在排序
        if (curMenuId == null) curMenuId = -1; //设置默认为顶层
        List<UserMenuVO> list = new ArrayList<>();
        System.out.println(map);
        for (Integer menuId : map.keySet()) {
            System.out.println(menuId);
            if (curMenuId.equals(map.get(menuId).getMenuParentId())){  //是子节点的数据
                UserMenuVO userMenuVO = new UserMenuVO();
                userMenuVO.setMenuId(menuId);
                userMenuVO.setDescription(map.get(menuId).getDescription());
                userMenuVO.setMenuName(map.get(menuId).getMenuName());
                userMenuVO.setMenuParentId(map.get(menuId).getMenuParentId());
                userMenuVO.setMust(map.get(menuId).isMust());
                userMenuVO.setHide(map.get(menuId).isHide());
                userMenuVO.setRoute(map.get(menuId).getRoute());
                userMenuVO.setIcon(map.get(menuId).getIcon());
                userMenuVO.setMenuType(map.get(menuId).getMenuType());
                userMenuVO.setSequence(map.get(menuId).getSequence());
                // 不存在排序
                if (map.get(menuId).getSequence() == null) flag = false;
                userMenuVO.setUpdateTime(map.get(menuId).getUpdateTime());
                //设置 子菜单
                userMenuVO.setUserMenuVOList(recursionGetMenu(map, menuId));
                list.add(userMenuVO);
            }
        }
        if (flag){  //存在排序时
            //排序显示
            Collections.sort(list, new Comparator<UserMenuVO>() {
                @Override
                public int compare(UserMenuVO o1, UserMenuVO o2) {
                    return o1.getSequence().compareTo(o2.getSequence());
                }
            });
        }
        return list;
    }

    @Override
    public FindUserVO findUserById(Integer userId) {
        SysUserDO sysUserDO = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUserDO>()
                .eq(SysUserDO::getId, userId));
        FindUserVO findUserVO = new FindUserVO();
        findUserVO.setUserId(sysUserDO.getId());
        findUserVO.setUserName(sysUserDO.getName());
        findUserVO.setPassword(sysUserDO.getPassword());
        findUserVO.setDescription(sysUserDO.getDescription());
        return findUserVO;
    }
}
